use crate::konst::PAGE_SIZE;

/// 页面ID使用u32.
/// 用于索引数据库中某个关系(表或索引)中的一个页面.
///
/// 如果使用4K大小的页面, 那么可以得到
/// 单个关系的文件大小最大是2 ^ 32 * 4KB = 16TB.
/// 这也正好是ext4文件系统最大的文件大小.
/// 如果以后需要进一步拓展, 可以将考虑将表的数据文件进行分割.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct PageID(u32);

impl PageID {

    #[inline]
    pub fn to_raw(&self) -> u32 {
        self.0
    }

    #[inline]
    pub fn offset(&self) -> usize {
        self.0 as usize * PAGE_SIZE
    }
}

impl From<u32> for PageID {
    fn from(v: u32) -> Self {
        Self(v)
    }
}

/// 关系ID使用u32.
/// 用于索引数据库中某个关系.
/// 数据库中每个表或索引都有一个唯一的[`RelationID`]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct RelationID(u32);

impl From<u32> for RelationID {
    fn from(v: u32) -> Self {
        Self(v)
    }
}

impl Into<u32> for RelationID {
    fn into(self) -> u32 {
        self.0
    }
}

/// 全局页面ID.
/// 用于索引数据库中任意一个页面.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct FullPageID(u64);

impl FullPageID {

    pub const INVALID: Self = Self(u64::MAX);

    #[inline]
    pub fn new(relation: RelationID, page: PageID) -> Self {
        Self(((relation.0 as u64) << 32) + page.0 as u64)
    }

    #[inline]
    pub fn relation_id(&self) -> RelationID {
        ((self.0 >> 32) as u32).into()
    }

    #[inline]
    pub fn page_id(&self) -> PageID {
        (self.0 as u32).into()
    }

    #[inline]
    pub fn set_relation(&mut self, relation: RelationID) {
        self.0 = (self.0 & 0x00000000FFFFFFFF) | ((relation.0 as u64) << 32);
    }

    #[inline]
    pub fn set_page_id(&mut self, page: PageID) {
        self.0 = (self.0 & 0xFFFFFFFF00000000) | (page.0 as u64);
    }
}

#[cfg(test)]
mod test {
    use super::{FullPageID, PageID, RelationID};

    /// 测试创建全局页面ID [`FullPageID`],
    /// 测试创建后的全局页面ID可以正确获得关系ID和页面ID.
    #[test]
    fn full_page_id_new_loopback() {
        let test_cases: Vec<(RelationID, PageID)> = vec![
            (101.into(), 10.into()),
            (1.into(), 10.into()),
            (u32::MAX.into(), u32::MAX.into()),
            (u32::MIN.into(), u32::MAX.into()),
            (u32::MAX.into(), u32::MIN.into()),
            (u32::MIN.into(), u32::MIN.into()),
        ];

        for (relation, page) in test_cases {
            let full = FullPageID::new(relation, page);
            assert_eq!(relation, full.relation_id());
            assert_eq!(page, full.page_id());
        }
    }

    /// 测试更改全局页面ID [`FullPageID`],
    /// 测试更改后的全局页面ID可以正确获得关系ID和页面ID.
    #[test]
    fn full_page_id_set_loopback() {
        let test_cases: Vec<(RelationID, PageID)> = vec![
            (101.into(), 10.into()),
            (1.into(), 10.into()),
            (u32::MAX.into(), u32::MAX.into()),
            (u32::MIN.into(), u32::MAX.into()),
            (u32::MAX.into(), u32::MIN.into()),
            (u32::MIN.into(), u32::MIN.into()),
        ];

        let relation_to_change: Vec<RelationID> = vec![
            u32::MAX.into(),
            u32::MIN.into(),
            1000.into(),
            1500.into(),
            2000.into(),
            1.into(),
            (u32::MAX - 1).into(),
            (u32::MAX - 1800).into(),
        ];

        let page_to_change: Vec<PageID> = vec![
            u32::MAX.into(),
            u32::MIN.into(),
            1000.into(),
            1500.into(),
            2000.into(),
            1.into(),
            (u32::MAX - 1).into(),
            (u32::MAX - 1800).into(),
        ];

        for (relation, page) in test_cases {
            let mut full = FullPageID::new(relation, page);
            for relation_to_change in relation_to_change.iter().cloned() {
                let old_page = full.page_id();
                full.set_relation(relation_to_change);

                assert_eq!(relation_to_change, full.relation_id());
                assert_eq!(old_page, full.page_id());

                for page_to_change in page_to_change.iter().cloned() {
                    full.set_page_id(page_to_change);
                    assert_eq!(page_to_change, full.page_id());
                    assert_eq!(relation_to_change, full.relation_id());
                }
            }
        }
    }
}
